#include "pch.hpp"
#include "ProjectModel.hpp"

using namespace Engine;
using namespace Engine::Edit;




ProjectModel::ProjectModel(Project::ptr project)
	: m_project(project)
{
	verify(project != nullptr);

	m_project->modified.connect(boost::bind(&ProjectModel::projectModified, this, _2, _3));

	m_icons[ProjectItem::FolderType]  = QPixmap(":/icons/folder");
	m_icons[ProjectItem::ScriptType]  = QPixmap(":/icons/script");
	m_icons[ProjectItem::UiType]      = QPixmap(":/icons/layout");
	m_icons[ProjectItem::ProjectType] = QPixmap(":/icons/application_form");
}
///////////////////////////////////////////////////////////////////////////////////////////////////

ProjectModel::~ProjectModel()
{}
///////////////////////////////////////////////////////////////////////////////////////////////////




QModelIndex ProjectModel::indexFromItem(ProjectItem::ptr item) const
{
	if(!item)
		return QModelIndex();

	auto parent = item->parent();
	if(!parent)
		return createIndex(0, 0, item.get());
	else
		return createIndex(parent->index(item), 0, item.get());
}
///////////////////////////////////////////////////////////////////////////////////////////////////

ProjectItem::ptr ProjectModel::itemFromIndex(const QModelIndex& index) const
{
	if(!index.isValid())
		return ProjectItem::ptr();

	ProjectItem* item = reinterpret_cast<ProjectItem*>(index.internalPointer());
	if(!item)
		return ProjectItem::ptr();

	return item->shared_from_this();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

QModelIndex ProjectModel::index(int row, int column, const QModelIndex& parent) const
{
	// Invalid row / column specifications shall return QModelIndex()
	if(row < 0 || column < 0 || row >= rowCount(parent) || column >= columnCount(parent))
		return QModelIndex();

	if(!parent.isValid())
		return createIndex(row, column, m_project.get());

	auto parentItem = itemFromIndex(parent);
	if(!parentItem)
		return QModelIndex();

	// Find the nth item
	auto item = parentItem->child(row);
	return createIndex(row, column, item.get());
}
///////////////////////////////////////////////////////////////////////////////////////////////////

QModelIndex ProjectModel::parent(const QModelIndex& index) const
{
	if(!index.isValid())
		return QModelIndex();

	auto item = itemFromIndex(index);
	auto parent = item->parent();

	return indexFromItem(parent);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

int ProjectModel::rowCount(const QModelIndex& parent) const
{
	if(!parent.isValid())
		return 1;

	auto item = itemFromIndex(parent);
	if(item)
		return item->numChildren();

	return 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

int ProjectModel::columnCount(const QModelIndex& parent) const
{
	return 1;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

Qt::ItemFlags ProjectModel::flags(const QModelIndex& index) const
{
	Qt::ItemFlags flags = __super::flags(index);

	auto item = itemFromIndex(index);
	if(item)
	{
		if(boost::shared_dynamic_cast<Project>(item) ||
			boost::shared_dynamic_cast<Script>(item) ||
			boost::shared_dynamic_cast<UiDefinition>(item))
			flags |= Qt::ItemIsEditable;
	}

	return flags;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

QVariant ProjectModel::data(const QModelIndex& index, int role) const
{
	auto item = itemFromIndex(index);
	if(!item)
		return QVariant();

	switch(role)
	{
	case Qt::EditRole:
	case Qt::DisplayRole:
		return item->name();

	case Qt::DecorationRole:
		{
			auto it = m_icons.find(item->type());
			if(it != m_icons.end())
				return it->second;
			// No break on purpose
		}

	default:
		return QVariant();
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////

bool ProjectModel::setData(const QModelIndex& index, const QVariant& data, int role)
{
	if(role != Qt::EditRole)
		return false;

	if(!data.canConvert<QString>())
		return false;

	auto item = itemFromIndex(index);
	if(!item)
		return false;

	QString name = data.value<QString>();

	auto parent = item->parent();
	if(parent)
	{
		// Ask the parent if this name is allowed...
		auto child = parent->child(name);
		if(child)
			return false;
	}

	item->setName(name);

	emit modified();
	return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////




ProjectItem::Result ProjectModel::projectModified(ProjectItem::weak_ptr item, ProjectItem::Action action)
{
	switch(action)
	{
	case ProjectItem::BeginAddChild:    beginAddChild(item);    break;
	case ProjectItem::EndAddChild:      endAddChild(item);      break;
	case ProjectItem::BeginRemoveChild: beginRemoveChild(item); break;
	case ProjectItem::EndRemoveChild:   endRemoveChild(item);   break;
	}

	return ProjectItem::Agree;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void ProjectModel::beginAddChild(ProjectItem::weak_ptr _item)
{
	auto item   = _item.lock();
	if(!item)
		return;

	auto parent = item->parent();
	if(!parent)
		return;

	auto index = indexFromItem(parent);
	beginInsertRows(index, parent->numChildren(), parent->numChildren());
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void ProjectModel::endAddChild(ProjectItem::weak_ptr _item)
{
	auto item   = _item.lock();
	if(!item)
		return;

	endInsertRows();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void ProjectModel::beginRemoveChild(ProjectItem::weak_ptr _item)
{
	auto item   = _item.lock();
	if(!item)
		return;

	auto parent = item->parent();
	if(!parent)
		return;

	auto index = indexFromItem(parent);
	auto row   = parent->index(item);
	beginRemoveRows(index, row, row);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void ProjectModel::endRemoveChild(ProjectItem::weak_ptr _item)
{
	auto item   = _item.lock();
	if(!item)
		return;

	endRemoveRows();
}
///////////////////////////////////////////////////////////////////////////////////////////////////
